#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
 (c) 2023 - Copyright CTyunOS Inc

 Authors:
   youyifeng <youyf2@chinatelecom.cn>

"""

import logging
import os
import sys
from logging.handlers import RotatingFileHandler
from optparse import OptionParser

import cve_ease as ease
from cve_ease import to_list

# from cve_ease.commands import *
from cve_ease.commands import *

categories = {
    'basic': 'basic commands',
    'info': 'info commands',
    'notifier': 'notifier commands',
    'test': 'test tool commands',
}


def get_extend_helpinfo(progname=None):
    if progname is None:
        progname = os.path.basename(sys.argv[0]) or 'cve-ease'
    categories_ordered = ', '.join(sorted(to_list(categories.keys())))
    extend_helpinfo = '''
Try "%(progname)s --help" for help about global gconfig
Try "%(progname)s help" to get all available commands
Try "%(progname)s <command> --help" for help about the gconfig of a particular command
Try "%(progname)s help <category>" to get commands under a particular category
Available commands are: %(categories)s
''' % ({'progname': progname, 'categories': categories_ordered})
    return extend_helpinfo


def get_gconfig():
    """process gconfig from command line and config file"""

    common_commands = [
        'cve', 'help', 'sa',
        'config', 'wecom', 'dingding',
        'feishu', 'rpm', 'cvrf',
        'service', 'repodata',
        'mail163', 'mailqq',
        'daemon', 'logger', 'db', 'motd',
        'baseline', 'autoupdate', 'nvd',
        'watcher', 'sss', 'smash', 'kernel',
    ]
    usage = "%%prog [global-gconfig] command [command-gconfig-and-arguments]\n\n" \
            "Common commands: %s" % ', '.join(sorted(common_commands))
    parser = OptionParser(usage=usage)
    parser.disable_interspersed_args()

    parser.__dict__['origin_format_help'] = parser.format_help
    parser.__dict__['format_help'] = lambda formatter=None: (
            "%(origin_format_help)s%(extend_helpinfo)s" % ({
        'origin_format_help': parser.origin_format_help(formatter),
        'extend_helpinfo': get_extend_helpinfo(progname='cve-ease')}))

    parser.add_option("-c", "--config", dest="configFile",
                      default="/etc/cve-ease/cve-ease.cfg",
                      help="Load settings from config file",
                      metavar="FILE")
    parser.add_option("-d", "--debug", dest='debug', action="store_true", default=False, help="Show debug output")
    parser.add_option("-q", "--quiet", dest='quiet', action="store_true", default=False, help="Run quietly")
    parser.add_option("--help-commands", action="store_true", default=False, help="List commands")
    # sql gconfig
    parser.add_option("--dbtype", dest="DB_TYPE", default=None, help="SQL database type.")
    parser.add_option("--dbfilepath", dest="DB_FILE_PATH", default=None,
                      help="SQL db file path (only when dbtype is sqlite).")
    parser.add_option("--dbuser", dest="DB_USER", default=None, help="SQL database username")
    parser.add_option("--dbpassword", dest="DB_PASSWORD", default=None, help="SQL database password")
    parser.add_option("--dbhost", dest="DB_HSOT", default=None, help="SQL database server hostname")
    parser.add_option("--dbport", dest="DB_PORT", default=None, help="SQL database server port")

    (gconfig, args) = parser.parse_args()

    # load local config
    try:
        if not os.path.exists(gconfig.configFile):
            raise FileNotFoundError(f"Config file not found: {gconfig.configFile}")
            
        # support for secret config
        defconfig = gconfig.configFile
        secret_config = defconfig + '.secret'
        if os.path.exists(secret_config):
            defconfig = secret_config
        setattr(gconfig, 'CONFIGFILE', defconfig)
        config = ease.load_config(configfile=defconfig)

    except FileNotFoundError as e:
        print(e)
        exit(1)
    except ease.ConfigurationError as e:
        print(f"Error loading configuration: {e}")
        exit(1)

    # update gconfig according to configfile
    for (name, value) in config['main'].items():
        if getattr(gconfig, name.upper(), None) is None:
            setattr(gconfig, name.upper(), value)
    gconfig.config = config

    if not args:
        gconfig.help_commands = True

    if gconfig.help_commands:
        return gconfig, '_list_commands', [0, '']

    cmd = args[0]

    cmd = cmd.replace('-', '_')
    # print("cmd:", cmd)
    if ('anon_handle_' + cmd) in globals():
        if not gconfig.force_auth and '--mine' not in args:
            gconfig.noauth = True
        cmd = 'anon_handle_' + cmd
    elif ('handle_' + cmd) in globals():
        cmd = 'handle_' + cmd
    else:
        return gconfig, '_list_commands', [1, 'Unknown command: %s' % args[0]]

    return gconfig, cmd, args[1:]


def handle_help(gconfig, session, args):
    "[info] List available commands"
    usage = "usage: %prog help <commands> ..."
    usage += "\n(Specify the --help global option for a list of other help gconfig)"
    parser = OptionParser(usage=usage)
    (gconfig, args) = parser.parse_args(args)
    chosen = set(args)
    avail = set(list(categories.keys()))
    unavail = chosen - avail
    for arg in unavail:
        print("No such help category: %s" % arg)

    if not chosen:
        list_commands()
    else:
        list_commands(chosen)


def check_python_version():
    current_python = sys.version_info[0]
    if current_python == 3:
        return
    else:
        raise ease.GenericError('Invalid python version requested: %d' % current_python)


def list_commands(categories_chosen=None):
    if categories_chosen is None or "all" in categories_chosen:
        categories_chosen = list(categories.keys())
    else:
        categories_chosen = list(categories_chosen)
    categories_chosen.sort()
    handlers = []
    for name, value in globals().items():
        if name.startswith('handle_'):
            alias = name.replace('handle_', '')
            alias = alias.replace('_', '-')
            handlers.append((alias, value))
        elif name.startswith('anon_handle_'):
            alias = name.replace('anon_handle_', '')
            alias = alias.replace('_', '-')
            handlers.append((alias, value))
    handlers.sort()
    print("Available commands:")
    for category in categories_chosen:
        print("\n%s:" % categories[category])
        for alias, handler in handlers:
            desc = handler.__doc__ or ''
            if desc.startswith('[%s] ' % category):
                desc = desc[len('[%s] ' % category):]
            elif category != 'misc' or desc.startswith('['):
                continue
            print("      %-25s %s" % (alias, desc))

    print("%s" % get_extend_helpinfo().rstrip("\n"))


if __name__ == "__main__":
    global gconfig
    # check run in correct python version
    check_python_version()
    # parse config from cmdline and configfile
    gconfig, command, args = get_gconfig()
    # make sure workdir exists
    ease.ensuredir(gconfig.WORKDIR)
    # log configuration
    logger = logging.getLogger("cve-ease")
    console_handler = logging.StreamHandler(sys.stderr)
    console_handler.setFormatter(logging.Formatter('%(asctime)s [%(levelname)s] %(name)s: %(message)s'))
    logger.addHandler(console_handler)

    if gconfig.LOG_FILE_PATH:
        logfile = os.path.join(gconfig.WORKDIR, gconfig.LOG_FILE_PATH)
        file_handler = RotatingFileHandler(
            filename=logfile,
            encoding='UTF-8',
            maxBytes=int(gconfig.LOG_MAXBYTES),
            backupCount=int(gconfig.LOG_BACKUP_NUM)
        )
        file_handler.setFormatter(logging.Formatter('%(asctime)s [%(levelname)s] %(name)s: %(message)s'))
        logger.addHandler(file_handler)

    if gconfig.debug:
        logger.setLevel(logging.DEBUG)
    elif gconfig.quiet:
        logger.setLevel(logging.ERROR)
    elif 'debug' == gconfig.LOG_LEVEL:
        logger.setLevel(logging.DEBUG)
    elif 'error' == gconfig.LOG_LEVEL:
        logger.setLevel(logging.ERROR)
    elif 'warn' == gconfig.LOG_LEVEL:
        logger.setLevel(logging.WARN)
    else:
        # set ERROR as default logger level
        logger.setLevel(logging.ERROR)

    # debug print gconfig
    logger.debug(f" * using config {gconfig.CONFIGFILE}")
    logger.debug(f" * config detail:")
    for ss in gconfig.config.sections():
        logger.debug(f"|-- section {ss}")
        for opt in gconfig.config[ss]:
            logger.debug("|    |-- %s : %s" % (opt, gconfig.config[ss][opt]))
    logger.debug("|________")

    if command == '_list_commands':
        list_commands()
        if args[0] != 0:
            logger.error(args[1])
        sys.exit(args[0])

    session = ease.SQLSession(gconfig)

    return_value = 0
    try:
        return_value = locals()[command].__call__(gconfig, session, args)
        if not return_value:
            return_value = 0
    except KeyboardInterrupt:
        return_value = 1
    except SystemExit:
        raise
    except Exception:
        raise
        # if gconfig.debug:
        #     raise
        # else:
        #     exctype, value = sys.exc_info()[:2]
        #     return_value = 1
        #     logger.error("%s: %s" % (exctype.__name__, value))
    try:
        session.close()
    except Exception:
        raise

    logger.debug(" * close sql session properly")
    sys.exit(return_value)
